package com.sunday.canal.adapter.mongo.config;

import com.mongodb.MongoClientSettings;
import com.sunday.canal.adapter.mongo.codecs.BigIntegerCodec;
import org.bson.BsonWriter;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecRegistries;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.autoconfigure.mongo.StandardMongoClientSettingsBuilderCustomizer;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: mongo client settings
 * <p>
 * 参考:
 * org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
 * <p>
 * 注意，从spring boot2.7开始，慢慢不支持META-INF/spring.factories文件了
 * 需要导入的自动配置类可以放在
 * /META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中，可以看下spring-boot-autoconfigure-2.7.0.jar下的这个文件
 * @Configuration或者@Configuration(proxyBeanMethods = true)的类被称为Full模式的配置类
 * （当然喽，proxyBeanMethods属性的默认值是true，所以一般需要Full模式我们只需要标个注解即可）
 * <p>
 * https://blog.csdn.net/tales522/article/details/117885968
 * <p>
 * full模式使用特性：
 * <p>
 * full模式下的配置类会被CGLIB代理生成代理类取代原始类型(在容器中)
 * full模式下的@Bean方法不能是private和final
 * 单例scope下不同@Bean方法可以互相引用，达到单实例的语义
 * lite模式使用特性：
 * <p>
 * lite模式下的配置类不生成代理，原始类型进入容器
 * lite模式下的@Bean方法可以是private和final
 * 单例scope下不同@Bean方法引用时无法做到单例
 * @Author: zsy
 * @Create: 2023/4/17 19:08
 */
@Configuration(proxyBeanMethods = false)
public class MongoClientSettingsConfiguration {

    /**
     * 解决 ：org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class java.math.BigInteger, types=null}.
     * 原因 ：因为 Document 所使用的值解析中没有 BigInteger 类型的解析器
     * 启动类 {@link org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration}
     *
     * @AutoConfiguration(after = MongoAutoConfiguration.class) {@link org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration.MongoClientSettingsConfiguration#mongoClientSettings()}
     * 通过 new MongoClientSettings(this) 进行配置 {@link MongoClientSettings.Builder#build()} 调用 {@link MongoClientSettings#getDefaultCodecRegistry()} 获取到默认配置 {@link MongoClientSettings#DEFAULT_CODEC_REGISTRY}
     * {@link org.bson.codecs.DocumentCodec#writeValue(BsonWriter, EncoderContext, Object)} 通过当时的默认注册 {@link org.bson.codecs.DocumentCodec#DEFAULT_REGISTRY} 进行解析
     * 最终在 {@link org.bson.codecs.ValueCodecProvider} 中没有获得 BigInteger 类型解析器，所以要自行进行添加
     */
    @Bean
    MongoClientSettings mongoClientSettings() {
        return MongoClientSettings
                .builder()
                .codecRegistry(
                        CodecRegistries.fromProviders(
                                CodecRegistries.fromCodecs(new BigIntegerCodec()),
                                MongoClientSettings.getDefaultCodecRegistry()))
                .build();
    }

    @Bean
    StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties, MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) {
        return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(), properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable());
    }

}